pub mod abi;
mod config;
pub mod pb;
use anyhow::Result;
pub use config::AppConfig;
use futures::Stream;
use pb::{
    user_stats_server::{UserStats, UserStatsServer},
    QueryRequest, RawQueryRequest, User,
};
use sqlx::PgPool;
use std::{ops::Deref, pin::Pin, sync::Arc};
use tonic::{async_trait, Request, Response, Status};
type ServiceResult<T> = std::result::Result<Response<T>, Status>;
type ResponseStream = Pin<Box<dyn Stream<Item = Result<User, Status>> + Send>>;

#[derive(Clone)]
pub struct UserStatsService {
    pub inner: Arc<UserStatsServiceInner>,
}

impl Deref for UserStatsService {
    type Target = UserStatsServiceInner;
    fn deref(&self) -> &Self::Target {
        &self.inner
    }
}
#[derive(Clone)]
pub struct UserStatsServiceInner {
    pub config: AppConfig,
    pub pool: PgPool,
}

#[async_trait]
impl UserStats for UserStatsService {
    type QueryStream = ResponseStream;
    type RawQueryStream = ResponseStream;
    async fn query(&self, req: Request<QueryRequest>) -> ServiceResult<Self::QueryStream> {
        let ret = self.query(req.into_inner()).await;
        Ok(Response::new(Box::pin(ret) as Self::QueryStream))
    }

    async fn raw_query(
        &self,
        request: Request<RawQueryRequest>,
    ) -> ServiceResult<Self::RawQueryStream> {
        let ret = self.raw_query(request.into_inner()).await;
        Ok(Response::new(Box::pin(ret) as Self::RawQueryStream))
    }
}

impl UserStatsService {
    pub async fn new() -> Self {
        let config = AppConfig::load().expect("load config failed");
        let pool = PgPool::connect(&config.server.db_url)
            .await
            .expect("connect db failed");

        Self {
            inner: Arc::new(UserStatsServiceInner { config, pool }),
        }
    }
    pub fn into_server(&self) -> UserStatsServer<Self> {
        UserStatsServer::new(self.clone())
    }
}
